﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RestSharp.Extensions;
using WH.Model.Systems;
using WH.Common.Config;
using WH.Common.DbConfig;
using SqlSugar;
using System.Reflection;
using System.Text;

namespace WH.Repositories.CodeFirst
{
    public class CodeFirstExtension
    {

        private readonly ILogger<CodeFirstExtension> _logger;
        private readonly ISqlSugarClient _db;
        private readonly IConfigurationCenter _configuration;


        ///// <summary>
        ///// 初始化结果文件
        ///// </summary>
        //private const string SuccessSeedDataFileName = "initcomplete.json";

        /// <summary>
        /// 种子数据文件夹
        /// </summary>
        private const string SeedDataDirName = "SeedData";

        /// <summary>
        /// 种子数据文件夹
        /// </summary>
        private const string InitialDataDirName = "InitialData";

        public string? NowVersion
        {
            get
            {
                return Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
            }
        }

        /// <summary>
        /// CodeFirst
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="db"></param>
        public CodeFirstExtension(
            ILogger<CodeFirstExtension> logger,
            ISqlSugarClient db,
            IConfigurationCenter configuration
            )
        {
            _logger = logger;
            _configuration = configuration;
            _db = db;

        }

        /// <summary>
        /// 初始化数据库、表、种子数据
        /// </summary>
        /// <param name="app"></param>
        public async Task InitDbTablesSeedData()
        {
            try
            {
                _db.DbMaintenance.CreateDatabase();

                //foreach (var entityType in BinderAttributeExtension.TableTypes)
                //{
                //    //创建表
                //    _db.CodeFirst.InitTables(entityType.Value);
                //}


                var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WH.Model.dll");

                Type[] types = Assembly
                    .LoadFrom(path)//如果 .dll报错，可以换成 xxx.exe 有些生成的是exe 
                    .GetTypes().Where(it => it.GetAttribute<SugarTable>() is not null)//命名空间过滤，可以写其他条件
                    .ToArray();//断点调试一下是不是需要的Type，不是需要的在进行过滤
                _db.CodeFirst.InitTables(types);


                var newVersion = await _db.Queryable<SysVersionEntity>().OrderByDescending(x => x.VersionNum).FirstAsync();

                if (newVersion is null)
                {
                    Console.WriteLine("检测到当前数据库未初始化，是否执行数据初始化？y：是 ");
                    var isConfirm = Console.ReadLine();

                    if (isConfirm?.Equals("y", StringComparison.OrdinalIgnoreCase) == true)
                    {
                        await SeedData();
                    }
                }

                if (!string.IsNullOrWhiteSpace(NowVersion))
                {
                    if (ConverVersionToInt(NowVersion) < ConverVersionToInt(newVersion?.Version ?? string.Empty))
                    {
                        await UpdateScriptExcuteAsync();
                    }

                    var nowVersion = new SysVersionEntity() { Version = NowVersion, VersionNum = ConverVersionToInt(NowVersion), UpdateTime = DateTime.Now };

                    await _db.Storageable(nowVersion).ExecuteCommandAsync();
                }
            }
            catch (Exception ex)
            {
                var msg = JsonConvert.SerializeObject(ex);
                _logger.LogError(msg);
                Console.WriteLine(msg);
                throw;
            }
        }

        /// <summary>
        /// 数据初始化到文件
        /// </summary>
        /// <returns></returns>
        public async Task GenerateSeedData()
        {
            //菜单
            var menus = await _db.Queryable<MenuEntity>().ToListAsync();
            await GenerateSeedDataJsonStr(menus);

            ////角色菜单权限
            //var menuRoles = await _db.Queryable<RolePermissionEntity>().Where(x => !string.IsNullOrWhiteSpace(x.OrganizationId)).Where(x => x.RoleId == "4205377769586167808").ToListAsync();
            //await GenerateSeedDataJsonStr(menuRoles);

            //角色
            var roles = await _db.Queryable<RoleEntity>().Where(x => !string.IsNullOrWhiteSpace(x.OrganizationId)).Where(x => x.TypeCode == "0").ToListAsync();
            await GenerateSeedDataJsonStr(roles);

            //账号
            var users = await _db.Queryable<UserEntity>().Where(x => !string.IsNullOrWhiteSpace(x.OrganizationId)).Where(x => x.OrganizationId == "0").ToListAsync();
            await GenerateSeedDataJsonStr(users);

            //账号登录
            var userLogons = await _db.Queryable<UserLogonEntity>().Where(x => !string.IsNullOrWhiteSpace(x.OrganizationId)).Where(x => x.OrganizationId == "0").ToListAsync();
            await GenerateSeedDataJsonStr(userLogons);
            ////机构
            //var orgs = await _db.Queryable<OrganizationEntity>().Where(x => x.Id == "999999999").ToListAsync();
            //await GenerateSeedDataJsonStr(orgs);

            //字典
            var keyValues = await _db.Queryable<KeyValueEntity>().Where(x => x.IsEnable).ToListAsync();
            await GenerateSeedDataJsonStr(keyValues);

            ////系统配置
            //var sysConfig = await _db.Queryable<AppConfigEntity>().ToListAsync();
            //await GenerateSeedDataJsonStr(sysConfig);

            //await SetSeedDataResult(new InitDataResult() { });
        }

        /// <summary>
        /// 生成数据Json文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        private async Task GenerateSeedDataJsonStr<T>(List<T> list)
        {
            // 获取泛型类的类型
            Type genericType = typeof(T);

            // 获取泛型类的类名
            string className = genericType.Name;

            var str = JsonConvert.SerializeObject(list);

            string filename = $"{className}.json";

            var basepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SeedDataDirName, InitialDataDirName, filename);

            var dirName= Path.GetDirectoryName(basepath);
            if (!Directory.Exists(dirName) && !string.IsNullOrWhiteSpace(dirName))
            {
                Directory.CreateDirectory(dirName);
            }

            FileStream fs = new FileStream(basepath, FileMode.Create, FileAccess.ReadWrite);

            using StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);

            await writer.WriteAsync(str);


            var logStr = $"\r\n" +
                $"【生成实体】：{className} \r\n" +
                $"【目标路径】：{basepath} \r\n";

            _logger.LogWarning(logStr);
        }

        /// <summary>
        /// 种子数据同步到数据库
        /// </summary>
        /// <param name="db"></param>
        public async Task SeedData()
        {
            //string filename = $"{className}.json";

            var basepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SeedDataDirName, InitialDataDirName);

            if (!Directory.Exists(basepath))
            {
                return;
            }

            string[] files = Directory.GetFiles(basepath, "*", SearchOption.AllDirectories);

            IEnumerable<Type> enumerable = from k in Assembly.Load("WH.Model").GetTypes()
                                           where k.IsDefined(typeof(SugarTable), inherit: true) && k.IsClass && !k.IsAbstract && !k.IsGenericType
                                           select k;

            foreach (var file in files)
            {
                var fileName = Path.GetFileNameWithoutExtension(file);

                var type = enumerable.Where(x => x.Name.Equals(fileName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                using var reader = new StreamReader(file);
                var str = await reader.ReadToEndAsync();

                if (type is null)
                {
                    continue;
                }


                Type genericListType = typeof(List<>).MakeGenericType(type);

                var data = JsonConvert.DeserializeObject(str, genericListType);

                var res = _db.StorageableByObject(data).ExecuteCommand();


                var logStr = $"\r\n" +
                    $"【同步数据】：{fileName} \r\n" +
                    $"【数据数量】：{res} \r\n";

                _logger.LogWarning(logStr);
            }
        }



        public async Task StartAsync(CancellationToken cancellationToken)
        {
            //await GenerateSeedData();

            try
            {
                await InitDbTablesSeedData();

            }
            catch (Exception exception)
            {
                var errStr = $"\r\n" +
                    $"【错误消息】：{exception.Message} \r\n" +
                    $"【异常类型】：{exception.GetType().Name} \r\n" +
                    $"【异常信息】：{exception.Message} \r\n" +
                    $"【堆栈调用】：{exception.StackTrace}";

                //采用log4net 进行错误日志记录
                _logger.LogError(errStr);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }


        /// <summary>
        /// 执行升级脚本
        /// </summary>
        /// <returns></returns>
        public async Task UpdateScriptExcuteAsync()
        {
            var list = versionDtos.Where(x => x.VersionNum > ConverVersionToInt(NowVersion) && x.VersionNum <= ConverVersionToInt(NowVersion)).OrderBy(x => x.VersionNum).ToList();

            foreach (var ver in list)
            {
                if (ver.UpdateScript is not null)
                {
                    ver.UpdateScript.Invoke();
                }

                var nowVersion = new SysVersionEntity() { Version = ConverVersionToString(ver.VersionNum), VersionNum = ver.VersionNum, UpdateTime = DateTime.Now };

                await _db.Storageable(nowVersion).ExecuteCommandAsync();
            }
        }

        private int ConverVersionToInt(string? version)
        {
            if (string.IsNullOrWhiteSpace(version))
            {
                return 0;
            }

            var versionArr = version.Split('.').Select(x => { return x.PadLeft(2, '0'); });

            var versionNum = int.Parse(string.Join("", versionArr));

            return versionNum;
        }

        private string ConverVersionToString(int version)
        {
            if (version < 01000000)
            {
                throw new ArgumentOutOfRangeException("version");
            }
            var list = new List<string>();
            var versionStr = version.ToString().PadLeft(8, '0');


            for (var i = 0; i < versionStr.Length - 1; i += 2)
            {
                list.Add((versionStr.Substring(i, i + 2)));
            }

            return string.Join(".", list);
        }

        List<VersionDto> versionDtos = new List<VersionDto>()
        {
            new VersionDto(){
            VersionNum = 01000001,
            UpdateScript = () =>{

                }
            },
            new VersionDto(){
            VersionNum = 01000002,
            UpdateScript = () =>{
                }
            }
        };

        private string getListCode<T>(List<T> list)
        {
            // 获取泛型类的类型
            Type genericType = typeof(T);

            // 获取泛型类的类名
            string className = genericType.Name;

            StringBuilder builder = new StringBuilder();

            builder.AppendLine($"new List<{className}>()\r\n            {{");

            foreach (var item in list)
            {
                var type = item.GetType();
                var propertys = type.GetProperties();

                builder.AppendLine($"new {className}(){{");
                foreach (var property in propertys)
                {
                    var attr = property.GetAttribute<SugarColumn>();

                    if (attr?.IsIgnore == true)
                    {
                        continue;
                    }

                    if (property.PropertyType == typeof(string))
                    {
                        builder.AppendLine($"{property.Name} = \"{property.GetValue(item)?.ToString()}\",");
                    }
                    if (property.PropertyType == typeof(int))
                    {
                        builder.AppendLine($"{property.Name} = {property.GetValue(item)?.ToString()},");
                    }
                    if (property.PropertyType == typeof(bool))
                    {
                        builder.AppendLine($"{property.Name} = {property.GetValue(item)?.ToString()?.ToLower()},");
                    }
                    if (property.PropertyType == typeof(DateTime))
                    {
                        if (property.GetValue(item) is not null && !string.IsNullOrWhiteSpace(property.GetValue(item)?.ToString()))
                        {
                            builder.AppendLine($"{property.Name} = DateTime.Parse(\"{property.GetValue(item)?.ToString()}\"),");
                        }
                        else
                        {
                            builder.AppendLine($"{property.Name} = default,");
                        }
                    }
                }

                builder.AppendLine(" },");
            }

            builder.AppendLine(" };");
            return builder.ToString().TrimEnd(',');
        }

    };

}

public class VersionDto
{
    /// <summary>
    /// 版本
    /// </summary>
    public int VersionNum { get; set; }

    /// <summary>
    /// 版本
    /// </summary>
    public string VersionString { get; set; } = null!;

    /// <summary>
    /// 升级脚本
    /// </summary>
    public Action UpdateScript { get; set; } = null!;
}

///// <summary>
///// 初始化结果
///// </summary>
//public class InitDataResult
//{
//    public string? NowVersion
//    {
//        get
//        {
//            return Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
//        }
//    }

//    //public bool IsSuccessInitData { get; set; }

//    /// <summary>
//    /// 初始化的数据库
//    /// </summary>
//    public List<SuccessInitDto> SuccessInitDbs { get; set; } = new List<SuccessInitDto>();

//    public class SuccessInitDto
//    {
//        public string DbConnectionString { get; set; } = null!;

//        public DateTime InitTime { get; set; }

//        public bool IsSuccessInit { get; set; }
//    }
//}
